﻿using Sasa.QualityTools.CodeAnalysis.Common;
using Sasa.QualityTools.CodeAnalysis.Core.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;

namespace Sasa.QualityTools.CodeAnalysis.Core.Utilities
{
    internal class MissingAssemblyResolver
    {
        private string cachePath;

        private Dictionary<string, string> assemblyPaths;


        internal MissingAssemblyResolver(string cachePath, IEnumerable<string> referenceAssemblyPaths)
        {
            this.cachePath = cachePath;
            assemblyPaths = new Dictionary<string, string>();

            foreach (var path in referenceAssemblyPaths)
            {
                try
                {
                    var name = AssemblyName.GetAssemblyName(path).FullName;
                    assemblyPaths[name] = path;
                }
                catch (Exception e)
                {
                    Logger.Warn("The assembly '{0}' can't be found. Reason = {1}", path, Text.Format(e));
                }
            }
        }


        internal void Resolve(IEnumerable<CodeAnalysisError> errors)
        {
            if (errors.Count(o => o.Id == "CA0058" || o.Id == "CA0060") == 0)
            {
                throw new MissingAssemblyResolutionException("The specified analysis errors don't contain information of assembly reference error.");
            }

            foreach (var error in errors.Where(o => o.Id == "CA0058" || o.Id == "CA0060"))
            {
                var assemblyName = FindAssemblyName(error.Message);
                if (!assemblyPaths.ContainsKey(assemblyName))
                {
                    throw new MissingAssemblyResolutionException("The list of referencing assemlbies doesn't contain '{0}'.", assemblyName);
                }

                var srcPath = assemblyPaths[assemblyName];
                var destPath = Path.Combine(cachePath, Path.GetFileName(srcPath));

                if (File.Exists(destPath))
                {
                    throw new MissingAssemblyResolutionException("'{0}' already exists.", destPath);
                }

                File.Copy(srcPath, destPath);
            }
        }

        private string FindAssemblyName(string message)
        {
            var match = Regex.Match(message, "'(?<AssemblyName>(?<Name>.+?), Version=(?<Version>\\d+?\\.\\d+?\\.\\d+?\\.\\d+?), Culture=(?<Culture>[\\w\\-]+), PublicKeyToken=(?<PublicKeyToken>\\w+))'");
            if (match.Success)
            {
                return match.Groups["AssemblyName"].Value;
            }

            match = Regex.Match(message, "'(?<AssemblyName>(?<Name>.+?), Version=(?<Version>\\d+?\\.\\d+?\\.\\d+?\\.\\d+?), Culture=(?<Culture>[\\w\\-]+))'");
            if (match.Success)
            {
                return match.Groups["AssemblyName"].Value + ", PublicKeyToken=null";
            }

            throw new MissingAssemblyResolutionException("The assembly name can't be found from '{0}'.", message);
        }
    }
}
